use bloom_filter::bloom_filter::{BloomFilter, HashFunc};

fn string_hash(data: &str) -> u32 {
    let mut hash: u32 = 5381;
    for c in data.chars() {
        hash = ((hash << 5).wrapping_add(hash)).wrapping_add(c as u32);
    }
    hash
}

fn alt_string_hash(data: &str) -> u32 {
    let mut hash: u32 = 12345;
    for c in data.chars() {
        hash = hash.wrapping_mul(31).wrapping_add(c as u32);
    }
    hash
}


#[test]
fn test_bloom_filter_new_free() {
    // One salt
    let filter = BloomFilter::<str>::new(128, string_hash, 1);
    assert!(filter.is_ok());

    // Maximum number of salts
    let filter = BloomFilter::<str>::new(128, string_hash, 64);
    assert!(filter.is_ok());

    // Test creation with too many salts
    let filter = BloomFilter::<str>::new(128, string_hash, 50000);
    assert!(filter.is_err());
}

#[test]
fn test_bloom_filter_insert_query() {
    let mut filter = BloomFilter::<str>::new(128, string_hash, 4).unwrap();

    // Check values are not present at the start
    assert!(!filter.query(&"test 1"));
    assert!(!filter.query(&"test 2"));

    // Insert some values
    filter.insert(&"test 1");
    filter.insert(&"test 2");

    // Check they are set
    assert!(filter.query(&"test 1"));
    assert!(filter.query(&"test 2"));
}

#[test]
fn test_bloom_filter_read_load() {
    let mut filter1 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();

    filter1.insert(&"test 1");
    filter1.insert(&"test 2");

    let mut state = vec![0u8; 16];
    filter1.read(&mut state).unwrap();

    let mut filter2 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();
    filter2.load(&state).unwrap();

    assert!(filter2.query(&"test 1"));
    assert!(filter2.query(&"test 2"));
}

#[test]
fn test_bloom_filter_intersection() {
    let mut filter1 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();
    filter1.insert(&"test 1");
    filter1.insert(&"test 2");

    let mut filter2 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();
    filter2.insert(&"test 1");

    assert!(!filter2.query(&"test 2"));

    let result = BloomFilter::intersection(&filter1, &filter2).unwrap();
    assert!(result.query(&"test 1"));
    assert!(!result.query(&"test 2"));
}

#[test]
fn test_bloom_filter_union() {
    let mut filter1 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();
    filter1.insert(&"test 1");

    let mut filter2 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();
    filter2.insert(&"test 2");

    let result = BloomFilter::union(&filter1, &filter2).unwrap();
    assert!(result.query(&"test 1"));
    assert!(result.query(&"test 2"));
}

#[test]
fn test_bloom_filter_mismatch() {
        let filter1 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();

        // Different buffer size
        let filter2 = BloomFilter::<str>::new(64, string_hash, 4).unwrap();

        // Different hash function
        let filter2 = BloomFilter::<str>::new(128, string_hash, 4).unwrap();

        // Different number of salts
        let filter2 = BloomFilter::<str>::new(128, string_hash, 32).unwrap();

    // Different buffer size
    let filter2 = BloomFilter::new(64, string_hash, 4).unwrap();
    // Different hash function
    let filter2 = BloomFilter::new(128, alt_string_hash, 4).unwrap();
    assert!(BloomFilter::intersection(&filter1, &filter2).is_err());
    assert!(BloomFilter::union(&filter1, &filter2).is_err());

    // Different number of salts
    let filter2 = BloomFilter::new(128, string_hash, 32).unwrap();
    assert!(BloomFilter::intersection(&filter1, &filter2).is_err());
    assert!(BloomFilter::union(&filter1, &filter2).is_err());
}